package edu.bw.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;

@Component
public class TransactionProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    @Autowired
    private ApplicationContext applicationContext;//注入Spring容器

    /**
     * @param cls 传递被代理的类
     * @return 例如: 传递过来ProductServiceImpl.class,将来该方法就返回ProductServiceImpl代理对象
     */
    @SuppressWarnings("unchecked") //不再检测该方法中的警告
    public Object createProxyInstance(Class cls) {
        if (cls.getInterfaces().length != 0) {//判断传递过来的被代理类(cls)是否实现了接口,如果实现了接口我们就使用JDK动态代理
            return Proxy.newProxyInstance(
                    cls.getClassLoader(),
                    cls.getInterfaces(),
                    /**
                     * proxy: 底层生成的代理类对象
                     * method: 通过代理类对象调用的方法封装城的method对象
                     * args: 通过代理类对象调用方法传递的参数
                     */
                    (proxy, method, args) -> {
                        Object retVal = null;//存储最终方法的返回值

                        if (method.getName().startsWith("find")) {//如果该条件为true,说明通过代理类对象调用的方法是以find开头的查询相关方法
                            retVal = method.invoke(applicationContext.getBean(cls), args); //相当于ProductServiceImpl对象调用find开头的方法
                        } else {
                            //执行else说明通过代理类对象调用是增删改(update,delete,insert)相关的方法我们才加入事务控制
                            try {
                                transactionManager.beginTransaction();
                                retVal = method.invoke(applicationContext.getBean(cls), args);//相当于通过ProductServiceImpl对象调用增删改方法
                                transactionManager.commit();
                            } catch (Exception e) {
                                transactionManager.rollback();
                                e.printStackTrace();
                            } finally {
                                transactionManager.release();
                            }

                        }

                        return retVal;
                    }
            );
        } else {
            //当前被代理的类没有实现任何父接口,此时使用CGLIB实现动态代理,将被代理类作为父类
            //1.利用Enhancer增强被代理类,通过它生成代理类对象
            Enhancer enhancer = new Enhancer();

            //2.设置将来的生成的代理类的父类
            enhancer.setSuperclass(cls);

            //3.设置回调函数,当通过代理类对象调用方法时候,转调回调函数
            enhancer.setCallback(

                    /**
                     *  obj: CGlib生成的代理类对象
                     *  method: 通过代理类对象调用方法,封装的Method对象
                     *  args: 通过代理类对象调用方法时传递的参数
                     *  proxy: 方法的代理对象
                      */
                    (MethodInterceptor)(obj, method, args, proxy) -> {
                        Object retVal = null;//存储最终方法的返回值

                        if (method.getName().startsWith("find")) {//如果该条件为true,说明通过代理类对象调用的方法是以find开头的查询相关方法
                            retVal = method.invoke(applicationContext.getBean(cls), args); //相当于ProductServiceImpl对象调用find开头的方法
                        } else {
                            //执行else说明通过代理类对象调用是增删改(update,delete,insert)相关的方法我们才加入事务控制
                            try {
                                transactionManager.beginTransaction();
                                retVal = method.invoke(applicationContext.getBean(cls), args);//相当于通过ProductServiceImpl对象调用增删改方法
                                transactionManager.commit();
                            } catch (Exception e) {
                                transactionManager.rollback();
                                e.printStackTrace();
                            } finally {
                                transactionManager.release();
                            }

                        }
                        return retVal;
                    }
            );

            //4.创建代理类对象并返回
            return enhancer.create();

        }
    }
}
